package com.lambert.gingko.watermark.strategy.impl;

import com.lambert.gingko.watermark.annotation.FileProcessor;
import com.lambert.gingko.watermark.enums.FileType;
import com.lambert.gingko.watermark.model.PositionParam;
import com.lambert.gingko.watermark.model.WatermarkConfig;
import com.lambert.gingko.watermark.strategy.AbstWatermarkProcessor;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameUtils;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;

/**
 * 视频水印处理器（文字转图片水印版）
 * 功能：将文字转换为图片水印，再添加到视频中，支持水印样式自定义
 */
@FileProcessor(FileType.VIDEO)
@Slf4j
public class VideoWatermarkProcessor extends AbstWatermarkProcessor {

    /**
     * 处理视频文件并添加水印
     * 支持两种模式：直接使用图片水印 或 将文字转换为图片水印
     */
    @Override
    protected void doProcess(InputStream is, File outputFile , String extension, WatermarkConfig config) throws Exception {
        //处理视频帧并添加水印
        processVideoFrames(is, outputFile, extension, config);
    }

    /**
     * 处理视频帧并添加水印
     */
    private void processVideoFrames(InputStream is, File outputFile, String format,WatermarkConfig config) throws Exception {
        try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(is)) {
            grabber.start();

            // 验证音视频流
            int videoStreamIdx = grabber.getVideoStream();
            int audioStreamIdx = grabber.getAudioStream();
            if (videoStreamIdx == -1 && audioStreamIdx == -1) {
                throw new IllegalArgumentException("输入文件不包含有效的音视频流");
            }

            // 初始化录制器
            try (FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(
                    outputFile,
                    grabber.getImageWidth(),
                    grabber.getImageHeight(),
                    grabber.getAudioChannels()
            )) {
                configureRecorder(recorder, grabber, format, videoStreamIdx);
                recorder.start();
                // 逐帧处理
                processFrames(grabber, recorder, config);

                recorder.stop();
            }
            grabber.stop();
        }
    }

    /**
     * 配置视频录制器参数
     */
    private void configureRecorder(FFmpegFrameRecorder recorder, FFmpegFrameGrabber grabber,
                                   String format, int videoStreamIdx) {
        recorder.setFormat(format);
        recorder.setFrameRate(grabber.getFrameRate());
        recorder.setVideoBitrate(grabber.getVideoBitrate());
        recorder.setAudioBitrate(grabber.getAudioBitrate());
        recorder.setSampleRate(grabber.getSampleRate());

        // 视频编码配置
        // 针对HEVC视频和AAC音频的兼容配置
        if (videoStreamIdx != -1) {
            // 输入是HEVC，输出使用H.264（兼容性更好，避免HEVC编码问题）
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            // 添加H.264编码参数（解决编码兼容问题）
            recorder.setOption("crf", "23"); // 恒定质量模式
            recorder.setOption("preset", "medium");
        }

        // 音频编码配置
        if (grabber.getAudioChannels() > 0) {
            recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
            recorder.setOption("b:a", "128k");
        }
    }

    /**
     * 处理视频帧
     */
    private  void processFrames(FFmpegFrameGrabber grabber,
                                      FFmpegFrameRecorder recorder,WatermarkConfig config) throws Exception {
        Frame frame;
        long startTime = System.currentTimeMillis();
        int frameCount = 0;

        while ((frame = grabber.grabFrame()) != null) {
            // 处理视频帧
            if (frame.image != null) {
                BufferedImage bufferedImage = Java2DFrameUtils.toBufferedImage(frame);
                BufferedImage watermarkedImage = addTextWatermarkToImage(
                        bufferedImage, config);
                Frame watermarkedFrame = Java2DFrameUtils.toFrame(watermarkedImage);
                watermarkedFrame.timestamp = frame.timestamp;
                recorder.record(watermarkedFrame);
                frameCount++;
            }

            // 处理音频帧
            if (frame.samples != null) {
                recorder.recordSamples(frame.sampleRate, frame.audioChannels, frame.samples);
            }
        }

        long endTime = System.currentTimeMillis();
        log.debug("处理完成，共处理 {} 帧，耗时 {} 毫秒",
                frameCount, (endTime - startTime));
    }

    /**
     * 给图片添加文本水印
     * 参考了文字水印添加的核心方法:cite[2]:cite[7]
     *
     * @param originalImage 原始图片
     * @param config 水印配置
     * @return 添加水印后的图片
     */
    public  BufferedImage addTextWatermarkToImage(
            BufferedImage originalImage,WatermarkConfig config) {

        // 创建具有相同尺寸的BufferedImage
        BufferedImage watermarkedImage = new BufferedImage(
                originalImage.getWidth(),
                originalImage.getHeight(),
                BufferedImage.TYPE_3BYTE_BGR // 使用TYPE_3BYTE_BGR兼容性更好:cite[6]
        );

        // 获取Graphics2D对象进行绘制
        Graphics2D g2d = watermarkedImage.createGraphics();

        // 绘制原始图像
        g2d.drawImage(originalImage, 0, 0, null);

        // 设置水印字体、颜色和透明度
        g2d.setFont(config.getFont());
        g2d.setColor(config.getColor());
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, config.getOpacity()));
        PositionParam positionParam = config.getPositionParam();
        positionParam.setEnclosingWidth(originalImage.getWidth());
        positionParam.setEnclosingHeight(originalImage.getHeight());
        Dimension textSize = getTextDimensions(config.getText(), config.getFont());
        positionParam.setWidth(textSize.width);
        positionParam.setHeight(textSize.height);
        Point point = config.getPosition().calculate(positionParam);
        // 绘制水印文字
        g2d.drawString(config.getText(), point.x, point.y);

        // 释放资源
        g2d.dispose();

        return watermarkedImage;
    }
    /**
     * 计算文本在指定字体下的尺寸
     * @param text 水印文本
     * @param font 水印字体
     * @return 文本尺寸
     */
    public static Dimension getTextDimensions(String text, Font font) {
        BufferedImage tempImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = tempImage.createGraphics();
        g2d.setFont(font);
        FontMetrics fm = g2d.getFontMetrics();
        int width = fm.stringWidth(text);
        int height = fm.getHeight();
        g2d.dispose();
        return new Dimension(width, height);
    }
    @Override
    public FileType getSupportedType() {
        return FileType.VIDEO;
    }
}
